【アップデート】LambdaがCloudWatch Application SignalsによるAPMをサポートするようになりました!
リテールアプリ共創部@大阪の岩田です。
2024/11/21付けのアップデートでLambdaがCloudWatch Application SignalsによるApplication Performance Monitoring(APM)をサポートするようになりました。
さっそく触ってみたので簡単に紹介させて頂きます。
アップデートの概要
これまでApplication Signalsでサポートされていたコンピューティング環境はEC2,ECS,EKSの3つでしたが、今回のアップデートによってLambdaも追加されました!
現時点では以下のランタイムがサポートされています。
- Python 3.10
- Python 3.11
- Python 3.12
- Python 3.13
- Node.js 18.x
- Node.js 20.x
- Node.js 22.x
注意点としてNode.jsについてはESモジュールはサポートされておらず、CommonJSのみサポートされています。
これはOpenTelemetry JavaScriptがESモジュールを正式サポートしていないためのようです。
仕組みとしてはAWSが用意するレイヤーをLambdaに紐づけ、各種環境変数を設定することでLambdaとApplication Signalsがよしなに連携してくれるというものです。
やってみる
それでは実際にLambdaとApplication Signalsを連携させていきます。今回はPython3.13のランタイムを利用しています。
最低限やることは以下の4つです。
- Lambda実行ロールに必要な権限を付与
CloudWatchLambdaApplicationSignalsExecutionRolePolicy
というマネージドポリシーが用意されているので、これを利用するのが楽です
- Lambdaの環境変数
AWS_LAMBDA_EXEC_WRAPPER
に/opt/otel-instrument
を設定 - AWSが提供するレイヤー
AWSOpenTelemetryDistroPython
/AWSOpenTelemetryDistroJs
を紐づけ - Lambdaの設定でApplication Signalsを有効化
- X-Rayのアクティブトレースも合わせて有効化するのが推奨されています
設定例としてはこんな感じになります。
この状態で以下のコードを実行してみます。
import json
import boto3
import requests
s3_client = boto3.client('s3')
dynamodb_client = boto3.client('dynamodb')
def lambda_handler(event, context):
s3_client.list_buckets()
dynamodb_client.list_tables()
requests.get('https://dev.classmethod.jp')
return {
'statusCode': 200,
'body': json.dumps('Hello from Lambda!')
}
※requests
を利用していますが、AWSOpenTelemetryDistroPython
のレイヤーにrequests
も内包されているため、特に追加のレイヤーを導入無しで利用可能になっています。
しばらく待つとApplication Signalsから対象のLambdaが確認できるようになります。
この辺りの情報はCW Logsのロググループ/aws/application-signals/data
に出力された結果をサマリして表示しているようです。ログの本体はこんな感じです。
X-Rayのトレース結果を確認すると以下の通りS3とDynamoDBへのアクセスもしっかりトレースできています。
従来はX-Ray SDKを利用してpatchを適用するなどの手順が必要でしたが、AWSOpenTelemetryDistroPython
のレイヤーがよしなに自動インストルメンテーションしてくれているので、Lambdaのコード上では特に手動のインストルメンテーション無しでトレースできています。
一方でrequests
を使用してhttps://dev.classmethod.jp
にアクセスしている部分はうまくトレースできていません。これはAWS_LAMBDA_EXEC_WRAPPER
に指定した初期処理の中で環境変数OTEL_PYTHON_DISABLED_INSTRUMENTATIONS
が自動的に設定されており、requests
の自動インストルメンテーションが無効化されているためです。該当箇所の処理は以下の通りです。
if [ -z ${OTEL_PYTHON_DISABLED_INSTRUMENTATIONS} ]; then
export OTEL_PYTHON_DISABLED_INSTRUMENTATIONS="aio-pika,aiohttp-client,aiohttp-server,aiopg,asgi,asyncio,asyncpg,boto,boto3,cassandra,celery,confluent_kafka,dbapi,django,elasticsearch,falcon,fastapi,flask,grpc_client,grpc_server,grpc_aio_client,grpc_aio_server,httpx,jinja2,kafka,logging,mysql,mysqlclient,pika,psycopg,psycopg2,pymemcache,pymongo,pymysql,pyramid,redis,remoulade,requests,sklearn,sqlalchemy,sqlite3,starlette,system_metrics,threading,tornado,tortoiseorm,urllib,urllib3,wsgi"
fi
export OTEL_PYTHON_DISABLED_INSTRUMENTATIONS="$OTEL_PYTHON_DISABLED_INSTRUMENTATIONS,aws-lambda";
この辺りの細かな仕様把握はレイヤーをダウンロードしてソースコードを読むのが手っ取り早いと思います。
レイヤーのソースコードはAWS CLIからget-layer-version-by-arn
を実行し、レスポンスに含まれるPresigned-URLからダウンロード可能です。例として以下のようなコマンドを実行すればPresigned-URLが取得できます。
aws lambda get-layer-version-by-arn --region us-east-1 --arn arn:aws:lambda:us-east-1:615299751070:layer:AWSOpenTelemetryDistroPython:5 | jq .Content.Location
ということでLambdaの環境変数OTEL_PYTHON_DISABLED_INSTRUMENTATIONS
を設定してrequestsも自動インストルメンテーションされるようにしてみましょう。設定する値はrequests
が入っていなければなんでも良いのですが、aws-lambda
は自動的にOTEL_PYTHON_DISABLED_INSTRUMENTATIONS
に追加されるようなので、ここではaws-lambda
を指定してみました。
改めてLambdaを実行してトレース結果を確認すると、今度はhttps://dev.classmethod.jp
へのリクエストもトレースできていることが分かります。
自動インストルメンテーションの対象は環境変数OTEL_PYTHON_DISABLED_INSTRUMENTATIONS
で調整していくと良さそうですね。
Application Signalsの「依存関係」からもdev.classmethod.jp
に関するメトリクスが見えるようになっています。
まとめ
簡単にですがLambda × Application Signalsを試してみました。
Lambdaのコード修正無しに自動インストルメンテーションできるのはお手軽かつ非常に便利ですね。クラウドサービスを活用したシステムにとってAPMは非常に重要なので、こういった便利な機能をうまく活用していきたいですね。